package gang.org.springframework.framework.metadata;

import gang.org.springframework.framework.core.annotation.GangAnnotationFilter;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;

//######################################################################
// GangMergedAnnotations 该接口，继承自Iterable接口
//######################################################################
final class GangTypeMappedAnnotations implements GangMergedAnnotations
{


    private final Object source;

    private final AnnotatedElement element;

    private final SearchStrategy strategy;

    private final Annotation[] annotations;

    private final GangRepeatableContainers repeatableContainers;

    private final GangAnnotationFilter annotationFilter;

    static final GangTypeMappedAnnotations NONE = new GangTypeMappedAnnotations(null, new Annotation[0], GangRepeatableContainers.none(), null);


    /**
     * @param element
     *      {@link com.gang.mars.boot.SpringbootManualApplication} ||
     *      {@link gang.org.springframework.boot.autoconfigure.GEnableAutoConfiguration}
     * */
    public GangTypeMappedAnnotations(AnnotatedElement element,
                                     SearchStrategy strategy,
                                     GangRepeatableContainers repeatableContainers,
                                     GangAnnotationFilter annotationFilter) {
        this.source = element;
        this.element = element;
        this.strategy = strategy;
        this.annotations = null;
        this.repeatableContainers = repeatableContainers;
        this.annotationFilter = annotationFilter;
    }

    /**
     * TODO
     * 业务意义 ?
     * @param source TODO
     * @param annotations TODO
     * @param repeatableContainers TODO
     * @param annotationFilter TODO
     * */
    private GangTypeMappedAnnotations(Object source,
                                      Annotation[] annotations,
                                      GangRepeatableContainers repeatableContainers,
                                      Object annotationFilter){
        this.source = source;
        this.element = null;
        this.strategy = null;
        this.annotations = annotations;
        this.repeatableContainers = null;
        this.annotationFilter = null;
    }

    @Override
    public Iterator<GangMergedAnnotation<Annotation>> iterator() {
        return null;
    }

    @Override
    public void forEach(Consumer<? super GangMergedAnnotation<Annotation>> action) {
        GangMergedAnnotations.super.forEach(action);
    }

    @Override
    public Spliterator<GangMergedAnnotation<Annotation>> spliterator() {
        return GangMergedAnnotations.super.spliterator();
    }

    /**
     * @param element {@link gang.org.springframework.boot.autoconfigure.GEnableAutoConfiguration}
     * */
    static GangMergedAnnotations from(AnnotatedElement element,
                                      SearchStrategy searchStrategy,
                                      GangRepeatableContainers repeatableContainers,
                                      GangAnnotationFilter annotationFilter)
    {
        return new GangTypeMappedAnnotations(element,searchStrategy,repeatableContainers,annotationFilter);
    }

    /**
     * @param annotationType 注解类的名称 String value "gang.org.springframework.boot.autoconfigure.GImport" 或其它
     * @param predicate default value is null
     * @param selector default value is null ，参考 GangMergedAnnotationSelector.firstDirectlyDeclared()
     * */
    @Override
    public <A extends Annotation> GangMergedAnnotation get(String annotationType, Predicate<? super GangMergedAnnotation<A>> predicate, GangMergedAnnotationSelector<A> selector)
    {

         //return GangMergedAnnotation -> GangTypeMappedAnnotation
        if(false){
            // TODO
        }
        GangMergedAnnotationFinder<A> processor = new GangMergedAnnotationFinder<>(annotationType, selector, predicate);

        /*
         * AnnotationsProcessor -> MergedAnnotationFinder实现类；C -> Object R -> MergedAnnotation impl by GangTypeMappedAnnotation
         * GangMergedAnnotation -> GangTypeMappedAnnotation
         * */
        GangMergedAnnotation<A> result = scan(annotationType, processor);

        return result;
    }

    @Override
    public boolean isPresent(String annotationType) {
        return false;
    }


    /**
     * TODO
     * A 首先该方法为一个泛型方法
     * @param criteria String value is "gang.org.springframework.boot.autoconfigure.GImport" 或其它
     * @param processor {@link GangMergedAnnotationFinder}
     * C - 输入参数 -> Object
     * R - 返回结果 -> MergedAnnotation
     * 由接口 AnnotationsProcessor -> 实现类 MergedAnnotationFinder 来明确
     * */
    private <C,R> R scan(C criteria, GangAnnotationsProcessor<C,R> processor)
    {
        if (false){
            //TODO
        }
        if (this.element != null && this.strategy != null) {
            R result = GangAnnotationsScanner.scan(criteria,this.element,this.strategy,processor);
            return result;
        }
        return null;
    }

    private class GangMergedAnnotationFinder<A extends Annotation> implements GangAnnotationsProcessor<Object,GangMergedAnnotation<A>>
    {
        private final Object requiredType;
        private final GangMergedAnnotationSelector<A> selector;
        private final Predicate<? super GangMergedAnnotation<A>> predicate;

        private GangMergedAnnotation<A> result;

        /**
         * @param requiredType String value is "gang.org.springframework.boot.autoconfigure.GImport"
         * @param selector default value is null
         * @param predicate default value is null
         * */
        public GangMergedAnnotationFinder(Object requiredType, GangMergedAnnotationSelector<A> selector, Predicate<? super GangMergedAnnotation<A>> predicate)
        {
            this.requiredType = requiredType;
            this.selector = selector;
            this.predicate = predicate;
        }

        @Override
        public GangMergedAnnotation<A> doWithAggregate(Object context, int aggregateIndex) {
            return null;
        }

        /**
         * @param context value is gang.org.springframework.boot.autoconfigure.GImport
         * @param aggregateIndex value is 0
         * @param source {@link gang.org.springframework.boot.autoconfigure.GEnableAutoConfiguration}
         * @param annotations {@link gang.org.springframework.boot.autoconfigure.GImport}
         * */
        @Override
        public GangMergedAnnotation<A> doWithAnnotations(Object context, int aggregateIndex, Object source, Annotation[] annotations) {

            for (Annotation annotation : annotations)
            {
                if (null != annotation && !annotationFilter.matches(annotation))
                {
                    GangMergedAnnotation<A> result = process(context,aggregateIndex,source,annotation);
                    if (result!=null)
                    {
                        return result;
                    }
                }
            }
            return null;
        }



        /**
         * @param type value is gang.org.springframework.boot.autoconfigure.GImport
         * @param aggregateIndex value is 0
         * @param source {@link gang.org.springframework.boot.autoconfigure.GEnableAutoConfiguration}
         * @param annotation {@link gang.org.springframework.boot.autoconfigure.GImport}
         * */
        //TODO
        private GangMergedAnnotation<A> process(Object type,
                                                int aggregateIndex,
                                                Object source,
                                                Annotation annotation)
        {
            GangAnnotationTypeMappings mappings = GangAnnotationTypeMappings.forAnnotationType(annotation.annotationType(),null,null);
            for (int i = 0; i < mappings.size(); i++) {
                GangAnnotationTypeMapping mapping =mappings.get(i);

                if (isMappingForType(mapping, annotationFilter, this.requiredType)) {
                    GangMergedAnnotation<A> candidate = GangTypeMappedAnnotation.createIfPossible(mapping,source,annotation,aggregateIndex,null);

                    if (false){
                        //TODO
                    }
                    updateLastResult(candidate);
                }
            }
            return null;
        }

        private void updateLastResult(GangMergedAnnotation<A> candidate)
        {
            GangMergedAnnotation<A> lastResult = this.result;

            this.result = lastResult != null ? null : candidate;
        }

        @Override
        public GangMergedAnnotation<A> finish(GangMergedAnnotation<A> result) {
            return result != null ? result : this.result;
        }
    }

    private static boolean isMappingForType(GangAnnotationTypeMapping mapping, GangAnnotationFilter annotationFilter, Object requiredType)
    {
        Class<? extends Annotation> actualType = mapping.getAnnotationType();

        //TODO

        boolean result = actualType == requiredType || actualType.getName().equals(requiredType);

        return result;
    }

    @Override
    public Stream<GangMergedAnnotation<Annotation>> stream() {
        //TODO
        return null;
    }
}
